/*
*  Copyright (C) 2009 Andrej Stepanchuk
*  Copyright (C) 2009-2010 Howard Chu
*
*  This file is part of librtmp.
*
*  librtmp is free software; you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as
*  published by the Free Software Foundation; either version 2.1,
*  or (at your option) any later version.
*
*  librtmp is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with librtmp see the file COPYING.  If not, write to
*  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*  Boston, MA  02110-1301, USA.
*  http://www.gnu.org/copyleft/lgpl.html
*/

#include <stdlib.h>
#include <string.h>

#include <assert.h>
#include <ctype.h>

#include "rtmp_sys.h"
#include "log.h"

int RTMP_ParseURL ( const char *url, int *protocol, AVal *host, unsigned int *port,
					AVal *playpath, AVal *app )
{
	char *p, *end, *col, *ques, *slash;

	RTMP_Log ( RTMP_LOGDEBUG, "Parsing..." );

	*protocol = RTMP_PROTOCOL_RTMP;
	*port = 0;
	playpath->av_len = 0;
	playpath->av_val = NULL;
	app->av_len = 0;
	app->av_val = NULL;

	/* Old School Parsing */

	/* look for usual :// pattern */
	p = strstr ( url, "://" );

	if ( !p )
	{
		RTMP_Log ( RTMP_LOGERROR, "RTMP URL: No :// in url!" );
		return FALSE;
	}

	{
		int len = ( int ) ( p - url );

		if ( len == 4 && strncasecmp ( url, "rtmp", 4 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMP;
		}
		else if ( len == 5 && strncasecmp ( url, "rtmpt", 5 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMPT;
		}
		else if ( len == 5 && strncasecmp ( url, "rtmps", 5 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMPS;
		}
		else if ( len == 5 && strncasecmp ( url, "rtmpe", 5 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMPE;
		}
		else if ( len == 5 && strncasecmp ( url, "rtmfp", 5 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMFP;
		}
		else if ( len == 6 && strncasecmp ( url, "rtmpte", 6 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMPTE;
		}
		else if ( len == 6 && strncasecmp ( url, "rtmpts", 6 ) == 0 )
		{
			*protocol = RTMP_PROTOCOL_RTMPTS;
		}
		else
		{
			RTMP_Log ( RTMP_LOGWARNING, "Unknown protocol!\n" );
			goto parsehost;
		}
	}

	RTMP_Log ( RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol );

parsehost:
	/* let's get the hostname */
	p += 3;

	/* check for sudden death */
	if ( *p == 0 )
	{
		RTMP_Log ( RTMP_LOGWARNING, "No hostname in URL!" );
		return FALSE;
	}

	end   = p + strlen ( p );
	col   = strchr ( p, ':' );
	ques  = strchr ( p, '?' );
	slash = strchr ( p, '/' );

	{
		int hostlen;

		if ( slash )
		{
			hostlen = slash - p;
		}
		else
		{
			hostlen = end - p;
		}

		if ( col && col - p < hostlen )
		{
			hostlen = col - p;
		}

		if ( hostlen < 256 )
		{
			host->av_val = p;
			host->av_len = hostlen;
			RTMP_Log ( RTMP_LOGDEBUG, "Parsed host    : %.*s", hostlen, host->av_val );
		}
		else
		{
			RTMP_Log ( RTMP_LOGWARNING, "Hostname exceeds 255 characters!" );
		}

		p += hostlen;
	}

	/* get the port number if available */
	if ( *p == ':' )
	{
		unsigned int p2;
		p++;
		p2 = atoi ( p );

		if ( p2 > 65535 )
		{
			RTMP_Log ( RTMP_LOGWARNING, "Invalid port number!" );
		}
		else
		{
			*port = p2;
		}
	}

	if ( !slash )
	{
		RTMP_Log ( RTMP_LOGWARNING, "No application or playpath in URL!" );
		return TRUE;
	}

	p = slash + 1;

	{
		/* parse application
		 *
		 * rtmp://host[:port]/app[/appinstance][/...]
		 * application = app[/appinstance]
		 */

		char *slash2, *slash3 = NULL;
		int applen, appnamelen;

		slash2 = strchr ( p, '/' );

		if ( slash2 )
		{
			slash3 = strchr ( slash2 + 1, '/' );
		}

		applen = end - p; /* ondemand, pass all parameters as app */
		appnamelen = applen; /* ondemand length */

		if ( ques && strstr ( p, "slist=" ) ) /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
		{
			appnamelen = ques - p;
		}
		else if ( strncmp ( p, "ondemand/", 9 ) == 0 )
		{
			/* app = ondemand/foobar, only pass app=ondemand */
			applen = 8;
			appnamelen = 8;
		}
		else   /* app!=ondemand, so app is app[/appinstance] */
		{
			if ( slash3 )
			{
				appnamelen = slash3 - p;
			}
			else if ( slash2 )
			{
				appnamelen = slash2 - p;
			}

			applen = appnamelen;
		}

		app->av_val = p;
		app->av_len = applen;
		RTMP_Log ( RTMP_LOGDEBUG, "Parsed app     : %.*s", applen, p );

		p += appnamelen;
	}

	if ( *p == '/' )
	{
		p++;
	}

	if ( end - p )
	{
		AVal av = {p, end - p};
		RTMP_ParsePlaypath ( &av, playpath );
	}

	return TRUE;
}

/*
* Extracts playpath from RTMP URL. playpath is the file part of the
* URL, i.e. the part that comes after rtmp://host:port/app/
*
* Returns the stream name in a format understood by FMS. The name is
* the playpath part of the URL with formatting depending on the stream
* type:
*
* mp4 streams: prepend "mp4:", remove extension
* mp3 streams: prepend "mp3:", remove extension
* flv streams: remove extension
*/
void RTMP_ParsePlaypath ( AVal *in, AVal *out )
{
	int addMP4 = 0;
	int addMP3 = 0;
	int subExt = 0;
	const char *playpath = in->av_val;
	const char *temp, *q, *ext = NULL;
	const char *ppstart = playpath;
	char *streamname, *destptr, *p;

	int pplen = in->av_len;

	out->av_val = NULL;
	out->av_len = 0;

	if ( ( *ppstart == '?' ) &&
			( temp = strstr ( ppstart, "slist=" ) ) != 0 )
	{
		ppstart = temp + 6;
		pplen = strlen ( ppstart );

		temp = strchr ( ppstart, '&' );

		if ( temp )
		{
			pplen = temp - ppstart;
		}
	}

	q = strchr ( ppstart, '?' );

	if ( pplen >= 4 )
	{
		if ( q )
		{
			ext = q - 4;
		}
		else
		{
			ext = &ppstart[pplen - 4];
		}

		if ( ( strncmp ( ext, ".f4v", 4 ) == 0 ) ||
				( strncmp ( ext, ".mp4", 4 ) == 0 ) )
		{
			addMP4 = 1;
			subExt = 1;
			/* Only remove .flv from rtmp URL, not slist params */
		}
		else if ( ( ppstart == playpath ) &&
				  ( strncmp ( ext, ".flv", 4 ) == 0 ) )
		{
			subExt = 1;
		}
		else if ( strncmp ( ext, ".mp3", 4 ) == 0 )
		{
			addMP3 = 1;
			subExt = 1;
		}
	}

	streamname = ( char * ) malloc ( ( pplen + 4 + 1 ) * sizeof ( char ) );

	if ( !streamname )
	{
		return;
	}

	destptr = streamname;

	if ( addMP4 )
	{
		if ( strncmp ( ppstart, "mp4:", 4 ) )
		{
			strcpy ( destptr, "mp4:" );
			destptr += 4;
		}
		else
		{
			subExt = 0;
		}
	}
	else if ( addMP3 )
	{
		if ( strncmp ( ppstart, "mp3:", 4 ) )
		{
			strcpy ( destptr, "mp3:" );
			destptr += 4;
		}
		else
		{
			subExt = 0;
		}
	}

	for ( p = ( char * ) ppstart; pplen > 0; )
	{
		/* skip extension */
		if ( subExt && p == ext )
		{
			p += 4;
			pplen -= 4;
			continue;
		}

		if ( *p == '%' )
		{
			unsigned int c;
			sscanf ( p + 1, "%02x", &c );
			*destptr++ = c;
			pplen -= 3;
			p += 3;
		}
		else
		{
			*destptr++ = *p++;
			pplen--;
		}
	}

	*destptr = '\0';

	out->av_val = streamname;
	out->av_len = destptr - streamname;
}
